package workflow

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/asynkron/protoactor-go/actor"
	"github.com/google/uuid"
	"github.com/louloulin/dataflare/pkg/actor/system"
)

// WorkflowSupervisor is an actor that supervises workflow instances
type WorkflowSupervisor struct {
	// workflows are the deployed workflows
	workflows map[string]*WorkflowDefinition
	// instances are the running workflow instances
	instances map[string]*WorkflowInstance
	// stateManager is the state manager PID
	stateManager *actor.PID
	// metricsCollector is the metrics collector PID
	metricsCollector *actor.PID
	// mu is a mutex for protecting the workflows and instances
	mu sync.RWMutex
	// logger is the workflow supervisor logger
	logger *log.Logger
}

// WorkflowDefinition represents a workflow definition
type WorkflowDefinition struct {
	// ID is the workflow ID
	ID string
	// Name is the workflow name
	Name string
	// Description is the workflow description
	Description string
	// Version is the workflow version
	Version string
	// Sources are the workflow sources
	Sources map[string]SourceDefinition
	// Processors are the workflow processors
	Processors map[string]ProcessorDefinition
	// Sinks are the workflow sinks
	Sinks map[string]SinkDefinition
	// Edges are the workflow edges
	Edges []EdgeDefinition
	// Config is the workflow configuration
	Config map[string]interface{}
}

// SourceDefinition represents a source definition
type SourceDefinition struct {
	// Type is the source type
	Type string
	// Config is the source configuration
	Config map[string]interface{}
}

// ProcessorDefinition represents a processor definition
type ProcessorDefinition struct {
	// Type is the processor type
	Type string
	// Config is the processor configuration
	Config map[string]interface{}
}

// SinkDefinition represents a sink definition
type SinkDefinition struct {
	// Type is the sink type
	Type string
	// Config is the sink configuration
	Config map[string]interface{}
}

// EdgeDefinition represents an edge definition
type EdgeDefinition struct {
	// From is the source node
	From NodeReference
	// To is the target node
	To NodeReference
}

// NodeReference represents a reference to a node
type NodeReference struct {
	// SourceID is the source ID
	SourceID string
	// ProcessorID is the processor ID
	ProcessorID string
	// SinkID is the sink ID
	SinkID string
}

// WorkflowInstance represents a workflow instance
type WorkflowInstance struct {
	// ID is the instance ID
	ID string
	// WorkflowID is the workflow ID
	WorkflowID string
	// Status is the instance status
	Status string
	// StartTime is when the instance started
	StartTime time.Time
	// EndTime is when the instance ended
	EndTime time.Time
	// Error is the error if the instance failed
	Error string
	// Parameters are the instance parameters
	Parameters map[string]interface{}
	// NodeActors are the node actors
	NodeActors map[string]*actor.PID
	// WorkflowActor is the workflow actor
	WorkflowActor *actor.PID
}

// NewWorkflowSupervisor creates a new workflow supervisor
func NewWorkflowSupervisor() actor.Actor {
	return &WorkflowSupervisor{
		workflows: make(map[string]*WorkflowDefinition),
		instances: make(map[string]*WorkflowInstance),
		logger:    log.New(log.Writer(), "[WorkflowSupervisor] ", log.LstdFlags),
	}
}

// Receive handles messages for the workflow supervisor
func (w *WorkflowSupervisor) Receive(context actor.Context) {
	switch msg := context.Message().(type) {
	case *actor.Started:
		w.handleStarted(context)
	case *actor.Stopping:
		w.handleStopping(context)
	case *actor.Terminated:
		w.handleTerminated(context, msg)
	case *DeployWorkflowCommand:
		w.handleDeployWorkflow(context, msg)
	case *UndeployWorkflowCommand:
		w.handleUndeployWorkflow(context, msg)
	case *StartWorkflowCommand:
		w.handleStartWorkflow(context, msg)
	case *StopWorkflowCommand:
		w.handleStopWorkflow(context, msg)
	case *GetWorkflowStatusCommand:
		w.handleGetWorkflowStatus(context, msg)
	case *ListWorkflowsCommand:
		w.handleListWorkflows(context)
	case *ListWorkflowInstancesCommand:
		w.handleListWorkflowInstances(context, msg)
	case *WorkflowCompletedMessage:
		w.handleWorkflowCompleted(context, msg)
	case *WorkflowFailedMessage:
		w.handleWorkflowFailed(context, msg)
	default:
		w.logger.Printf("Received unknown message: %T", msg)
	}
}

// handleStarted handles the Started message
func (w *WorkflowSupervisor) handleStarted(context actor.Context) {
	w.logger.Println("Workflow supervisor started")

	// Get state manager and metrics collector
	systemSupervisor, err := context.ActorSystem().Root.RequestFuture(
		actor.NewPID(context.ActorSystem().Address(), "system-supervisor"),
		&system.GetChildrenCommand{},
		5*time.Second,
	).Result()
	if err != nil {
		w.logger.Printf("Failed to get system supervisor children: %v", err)
		return
	}

	response, ok := systemSupervisor.(*system.GetChildrenResponse)
	if !ok {
		w.logger.Printf("Unexpected response type: %T", systemSupervisor)
		return
	}

	w.stateManager = response.Children["state-manager"]
	w.metricsCollector = response.Children["metrics-collector"]

	w.logger.Printf("State manager: %s", w.stateManager)
	w.logger.Printf("Metrics collector: %s", w.metricsCollector)
}

// handleStopping handles the Stopping message
func (w *WorkflowSupervisor) handleStopping(context actor.Context) {
	w.logger.Println("Workflow supervisor stopping")

	// Stop all workflow instances
	for _, instance := range w.instances {
		if instance.WorkflowActor != nil {
			context.Stop(instance.WorkflowActor)
		}
	}
}

// handleTerminated handles the Terminated message
func (w *WorkflowSupervisor) handleTerminated(context actor.Context, msg *actor.Terminated) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Find the terminated workflow instance
	for id, instance := range w.instances {
		if instance.WorkflowActor != nil && instance.WorkflowActor.String() == msg.Who.String() {
			w.logger.Printf("Workflow instance terminated: %s", id)
			
			// Update instance status
			instance.Status = "terminated"
			instance.EndTime = time.Now()
			
			// Report metrics
			if w.metricsCollector != nil {
				context.Send(w.metricsCollector, &system.ReportMetricCommand{
					Name:  "workflow.instances.terminated",
					Value: 1,
					Tags: map[string]string{
						"workflow_id": instance.WorkflowID,
					},
				})
			}
			
			break
		}
	}
}

// handleDeployWorkflow handles the DeployWorkflowCommand message
func (w *WorkflowSupervisor) handleDeployWorkflow(context actor.Context, msg *DeployWorkflowCommand) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if workflow already exists
	if _, exists := w.workflows[msg.WorkflowID]; exists {
		context.Respond(&DeployWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow already exists: %s", msg.WorkflowID),
		})
		return
	}

	// Convert definition to WorkflowDefinition
	definition, ok := msg.Definition.(*WorkflowDefinition)
	if !ok {
		// Try to convert from map
		if definitionMap, mapOk := msg.Definition.(map[string]interface{}); mapOk {
			definition = &WorkflowDefinition{
				ID:          msg.WorkflowID,
				Name:        definitionMap["name"].(string),
				Description: definitionMap["description"].(string),
				Version:     definitionMap["version"].(string),
				Config:      definitionMap["config"].(map[string]interface{}),
				// TODO: Convert sources, processors, sinks, and edges
			}
		} else {
			context.Respond(&DeployWorkflowResponse{
				Success: false,
				Error:   "invalid workflow definition type",
			})
			return
		}
	}

	// Store workflow definition
	w.workflows[msg.WorkflowID] = definition

	// Save workflow definition to state manager
	if w.stateManager != nil {
		future := context.RequestFuture(w.stateManager, &system.SaveStateCommand{
			Key:   "workflow:" + msg.WorkflowID,
			State: definition,
		}, 5*time.Second)

		result, err := future.Result()
		if err != nil {
			w.logger.Printf("Failed to save workflow definition: %v", err)
		} else {
			response, ok := result.(*system.SaveStateResponse)
			if !ok || !response.Success {
				w.logger.Printf("Failed to save workflow definition: %v", response.Error)
			}
		}
	}

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.deployments",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": msg.WorkflowID,
			},
		})
	}

	// Respond with success
	context.Respond(&DeployWorkflowResponse{
		Success: true,
	})

	w.logger.Printf("Deployed workflow: %s", msg.WorkflowID)
}

// handleUndeployWorkflow handles the UndeployWorkflowCommand message
func (w *WorkflowSupervisor) handleUndeployWorkflow(context actor.Context, msg *UndeployWorkflowCommand) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if workflow exists
	if _, exists := w.workflows[msg.WorkflowID]; !exists {
		context.Respond(&UndeployWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow not found: %s", msg.WorkflowID),
		})
		return
	}

	// Check if workflow has running instances
	for _, instance := range w.instances {
		if instance.WorkflowID == msg.WorkflowID && instance.Status == "running" {
			context.Respond(&UndeployWorkflowResponse{
				Success: false,
				Error:   fmt.Sprintf("workflow has running instances: %s", msg.WorkflowID),
			})
			return
		}
	}

	// Remove workflow definition
	delete(w.workflows, msg.WorkflowID)

	// Delete workflow definition from state manager
	if w.stateManager != nil {
		// TODO: Implement state deletion
	}

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.undeployments",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": msg.WorkflowID,
			},
		})
	}

	// Respond with success
	context.Respond(&UndeployWorkflowResponse{
		Success: true,
	})

	w.logger.Printf("Undeployed workflow: %s", msg.WorkflowID)
}

// handleStartWorkflow handles the StartWorkflowCommand message
func (w *WorkflowSupervisor) handleStartWorkflow(context actor.Context, msg *StartWorkflowCommand) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if workflow exists
	workflow, exists := w.workflows[msg.WorkflowID]
	if !exists {
		context.Respond(&StartWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow not found: %s", msg.WorkflowID),
		})
		return
	}

	// Create instance ID
	instanceID := uuid.New().String()

	// Create workflow instance
	instance := &WorkflowInstance{
		ID:         instanceID,
		WorkflowID: msg.WorkflowID,
		Status:     "starting",
		StartTime:  time.Now(),
		Parameters: msg.Parameters,
		NodeActors: make(map[string]*actor.PID),
	}

	// Store instance
	w.instances[instanceID] = instance

	// Create workflow actor
	props := actor.PropsFromProducer(func() actor.Actor {
		return NewWorkflowActor(workflow, instance, w.stateManager, w.metricsCollector)
	}).WithSupervisor(NewWorkflowSupervisorStrategy())

	workflowActor, err := context.SpawnNamed(props, "workflow-"+instanceID)
	if err != nil {
		delete(w.instances, instanceID)
		context.Respond(&StartWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("failed to spawn workflow actor: %v", err),
		})
		return
	}

	// Store workflow actor
	instance.WorkflowActor = workflowActor
	instance.Status = "running"

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.instances.started",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": msg.WorkflowID,
			},
		})
	}

	// Respond with success
	context.Respond(&StartWorkflowResponse{
		Success: true,
		RunID:   instanceID,
	})

	w.logger.Printf("Started workflow instance: %s for workflow: %s", instanceID, msg.WorkflowID)
}

// handleStopWorkflow handles the StopWorkflowCommand message
func (w *WorkflowSupervisor) handleStopWorkflow(context actor.Context, msg *StopWorkflowCommand) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if instance exists
	instance, exists := w.instances[msg.RunID]
	if !exists {
		context.Respond(&StopWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow instance not found: %s", msg.RunID),
		})
		return
	}

	// Check if instance is running
	if instance.Status != "running" {
		context.Respond(&StopWorkflowResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow instance not running: %s", msg.RunID),
		})
		return
	}

	// Stop workflow actor
	if instance.WorkflowActor != nil {
		context.Stop(instance.WorkflowActor)
	}

	// Update instance status
	instance.Status = "stopped"
	instance.EndTime = time.Now()

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.instances.stopped",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": instance.WorkflowID,
			},
		})
	}

	// Respond with success
	context.Respond(&StopWorkflowResponse{
		Success: true,
	})

	w.logger.Printf("Stopped workflow instance: %s", msg.RunID)
}

// handleGetWorkflowStatus handles the GetWorkflowStatusCommand message
func (w *WorkflowSupervisor) handleGetWorkflowStatus(context actor.Context, msg *GetWorkflowStatusCommand) {
	w.mu.RLock()
	defer w.mu.RUnlock()

	// Check if instance exists
	instance, exists := w.instances[msg.RunID]
	if !exists {
		context.Respond(&GetWorkflowStatusResponse{
			Success: false,
			Error:   fmt.Sprintf("workflow instance not found: %s", msg.RunID),
		})
		return
	}

	// Get node statuses
	nodeStatuses := make(map[string]string)
	// TODO: Get actual node statuses

	// Respond with status
	context.Respond(&GetWorkflowStatusResponse{
		Success:      true,
		Status:       instance.Status,
		Progress:     calculateProgress(instance),
		StartTime:    instance.StartTime,
		EndTime:      instance.EndTime,
		NodeStatuses: nodeStatuses,
	})
}

// handleListWorkflows handles the ListWorkflowsCommand message
func (w *WorkflowSupervisor) handleListWorkflows(context actor.Context) {
	w.mu.RLock()
	defer w.mu.RUnlock()

	// Create workflow list
	workflows := make([]*WorkflowInfo, 0, len(w.workflows))
	for id, workflow := range w.workflows {
		workflows = append(workflows, &WorkflowInfo{
			ID:          id,
			Name:        workflow.Name,
			Description: workflow.Description,
			Version:     workflow.Version,
		})
	}

	// Respond with workflows
	context.Respond(&ListWorkflowsResponse{
		Workflows: workflows,
	})
}

// handleListWorkflowInstances handles the ListWorkflowInstancesCommand message
func (w *WorkflowSupervisor) handleListWorkflowInstances(context actor.Context, msg *ListWorkflowInstancesCommand) {
	w.mu.RLock()
	defer w.mu.RUnlock()

	// Create instance list
	instances := make([]*WorkflowInstanceInfo, 0)
	for id, instance := range w.instances {
		if msg.WorkflowID == "" || instance.WorkflowID == msg.WorkflowID {
			instances = append(instances, &WorkflowInstanceInfo{
				ID:         id,
				WorkflowID: instance.WorkflowID,
				Status:     instance.Status,
				StartTime:  instance.StartTime,
				EndTime:    instance.EndTime,
				Error:      instance.Error,
			})
		}
	}

	// Respond with instances
	context.Respond(&ListWorkflowInstancesResponse{
		Instances: instances,
	})
}

// handleWorkflowCompleted handles the WorkflowCompletedMessage message
func (w *WorkflowSupervisor) handleWorkflowCompleted(context actor.Context, msg *WorkflowCompletedMessage) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if instance exists
	instance, exists := w.instances[msg.RunID]
	if !exists {
		w.logger.Printf("Workflow instance not found: %s", msg.RunID)
		return
	}

	// Update instance status
	instance.Status = "completed"
	instance.EndTime = time.Now()

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.instances.completed",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": instance.WorkflowID,
			},
		})
	}

	w.logger.Printf("Workflow instance completed: %s", msg.RunID)
}

// handleWorkflowFailed handles the WorkflowFailedMessage message
func (w *WorkflowSupervisor) handleWorkflowFailed(context actor.Context, msg *WorkflowFailedMessage) {
	w.mu.Lock()
	defer w.mu.Unlock()

	// Check if instance exists
	instance, exists := w.instances[msg.RunID]
	if !exists {
		w.logger.Printf("Workflow instance not found: %s", msg.RunID)
		return
	}

	// Update instance status
	instance.Status = "failed"
	instance.EndTime = time.Now()
	instance.Error = msg.Error

	// Report metrics
	if w.metricsCollector != nil {
		context.Send(w.metricsCollector, &system.ReportMetricCommand{
			Name:  "workflow.instances.failed",
			Value: 1,
			Tags: map[string]string{
				"workflow_id": instance.WorkflowID,
			},
		})
	}

	w.logger.Printf("Workflow instance failed: %s - %s", msg.RunID, msg.Error)
}

// calculateProgress calculates the workflow progress
func calculateProgress(instance *WorkflowInstance) int {
	// TODO: Implement actual progress calculation
	if instance.Status == "completed" {
		return 100
	} else if instance.Status == "running" {
		return 50
	}
	return 0
}

// NewWorkflowSupervisorStrategy creates a new workflow supervisor strategy
func NewWorkflowSupervisorStrategy() actor.SupervisorStrategy {
	return actor.NewOneForOneStrategy(5, 10*time.Second, func(reason interface{}) actor.Directive {
		switch reason.(type) {
		case *NodeFailedError:
			return actor.RestartDirective
		case *ConfigurationError:
			return actor.StopDirective
		default:
			return actor.EscalateDirective
		}
	})
}

// NodeFailedError represents a node failure error
type NodeFailedError struct {
	// NodeID is the ID of the failed node
	NodeID string
	// Error is the error message
	Error string
}

// Error returns the error message
func (e *NodeFailedError) Error() string {
	return fmt.Sprintf("node failed: %s - %s", e.NodeID, e.Error)
}

// ConfigurationError represents a configuration error
type ConfigurationError struct {
	// Message is the error message
	Message string
}

// Error returns the error message
func (e *ConfigurationError) Error() string {
	return fmt.Sprintf("configuration error: %s", e.Message)
}

// WorkflowInfo represents workflow information
type WorkflowInfo struct {
	// ID is the workflow ID
	ID string
	// Name is the workflow name
	Name string
	// Description is the workflow description
	Description string
	// Version is the workflow version
	Version string
}

// WorkflowInstanceInfo represents workflow instance information
type WorkflowInstanceInfo struct {
	// ID is the instance ID
	ID string
	// WorkflowID is the workflow ID
	WorkflowID string
	// Status is the instance status
	Status string
	// StartTime is when the instance started
	StartTime time.Time
	// EndTime is when the instance ended
	EndTime time.Time
	// Error is the error if the instance failed
	Error string
}

// DeployWorkflowCommand is a command to deploy a workflow
type DeployWorkflowCommand struct {
	// WorkflowID is the workflow ID
	WorkflowID string
	// Definition is the workflow definition
	Definition interface{}
}

// DeployWorkflowResponse is the response to a DeployWorkflowCommand
type DeployWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
}

// UndeployWorkflowCommand is a command to undeploy a workflow
type UndeployWorkflowCommand struct {
	// WorkflowID is the workflow ID
	WorkflowID string
}

// UndeployWorkflowResponse is the response to an UndeployWorkflowCommand
type UndeployWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
}

// StartWorkflowCommand is a command to start a workflow
type StartWorkflowCommand struct {
	// WorkflowID is the workflow ID
	WorkflowID string
	// Parameters are the workflow parameters
	Parameters map[string]interface{}
}

// StartWorkflowResponse is the response to a StartWorkflowCommand
type StartWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
	// RunID is the ID of the workflow run
	RunID string
}

// StopWorkflowCommand is a command to stop a workflow
type StopWorkflowCommand struct {
	// RunID is the ID of the workflow run
	RunID string
}

// StopWorkflowResponse is the response to a StopWorkflowCommand
type StopWorkflowResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
}

// GetWorkflowStatusCommand is a command to get workflow status
type GetWorkflowStatusCommand struct {
	// RunID is the ID of the workflow run
	RunID string
}

// GetWorkflowStatusResponse is the response to a GetWorkflowStatusCommand
type GetWorkflowStatusResponse struct {
	// Success indicates if the operation was successful
	Success bool
	// Error is the error message if the operation failed
	Error string
	// Status is the workflow status
	Status string
	// Progress is the workflow progress (0-100)
	Progress int
	// StartTime is when the workflow started
	StartTime time.Time
	// EndTime is when the workflow ended
	EndTime time.Time
	// NodeStatuses are the statuses of the workflow nodes
	NodeStatuses map[string]string
}

// ListWorkflowsCommand is a command to list workflows
type ListWorkflowsCommand struct{}

// ListWorkflowsResponse is the response to a ListWorkflowsCommand
type ListWorkflowsResponse struct {
	// Workflows are the workflows
	Workflows []*WorkflowInfo
}

// ListWorkflowInstancesCommand is a command to list workflow instances
type ListWorkflowInstancesCommand struct {
	// WorkflowID is the workflow ID to filter by
	WorkflowID string
}

// ListWorkflowInstancesResponse is the response to a ListWorkflowInstancesCommand
type ListWorkflowInstancesResponse struct {
	// Instances are the workflow instances
	Instances []*WorkflowInstanceInfo
}

// WorkflowCompletedMessage is a message for workflow completion
type WorkflowCompletedMessage struct {
	// RunID is the ID of the workflow run
	RunID string
}

// WorkflowFailedMessage is a message for workflow failure
type WorkflowFailedMessage struct {
	// RunID is the ID of the workflow run
	RunID string
	// Error is the error message
	Error string
}

// NewWorkflowActor creates a new workflow actor
func NewWorkflowActor(workflow *WorkflowDefinition, instance *WorkflowInstance, stateManager *actor.PID, metricsCollector *actor.PID) actor.Actor {
	// This is a placeholder implementation
	// The actual implementation will be in a separate file
	return &actor.EmptyActor{}
}
